var request = require('request'); /** * ## Responses * Responses from this API with be wrapped in an envelope to assist the caller * in dealing with the various responses from the service. There are no types * to check against in JavaScript, so this facilitates that. * * { * status: 200, * data: { name: "John", age: 29 } * } * * ## Logging * To enable logging, use the NODE_DEBUG environment variable. * * NODE_DEBUG=apidoc * * @constructor */ function IonrollerAPI (host) { var showLogMessages = process.env.NODE_DEBUG ? process.env.NODE_DEBUG.indexOf('apidoc') !== -1 : false; function log (message) { if (showLogMessages) { console.log('[apidoc] ' + message); } } function getQueryString (options) { var params = []; var qs = options.qs || {}; for (var key in qs) { if (qs[key]) { params.push(key + '=' + qs[key]); } } return params.join('&'); } function doRequest (options) { var startMs = Date.now(); options = options || {}; if (!options.json) { options.json = true; } options.qsStringifyOptions = { arrayFormat: 'repeat' }; var queryString = getQueryString(options); var url = options.uri + (queryString ? '?' + queryString : ''); log(options.method + ' ' + url); if (options.method === 'POST' || options.method === 'PUT' || options.method === 'PATCH') { log('|--> With body:\n\t\t' + JSON.stringify(options.json)); } return new Promise(function (resolve, reject) { request(options, function (err, response) { if (err) { reject(err); return; } var duration = Date.now() - startMs; var requestHref = response.request ? response.request.href : ''; log('Completed ' + options.method + ' ' + requestHref + ' ' + response.statusCode + ' ' + duration + 'ms'); resolve(response); }); }); } /** * Simple plain object check. This is actually really involved to be 100% * accurate */ function isObject (obj) { return typeof obj === 'object' && !Array.isArray(obj); } var Services = (function Services() { /** * Lists all services * */ function get() { var url = host + '/services'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * Returns service state (current and desired) * * @param {string} serviceName */ function getByServiceName(serviceName) { var url = host + '/services/' + serviceName + ''; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * Creates or updates service configuration * * @param {string} serviceName * @param {Object} options * @param {Object} options.data - The request body */ function putConfigByServiceName(serviceName, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } if (!options.data) { return Promise.reject(new Error('options.data property is required.')); } var url = host + '/services/' + serviceName + '/config'; var queryString = { }; var requestOpts = { qs: queryString, json: options.data, uri: url, method: 'PUT' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 204: return Promise.resolve(); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * Returns service configuration (all versions over time) * * @param {string} serviceName * @param {Object} options * @param {string} options.from * @param {string} options.to */ function getConfigsByServiceName(serviceName, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/services/' + serviceName + '/configs'; var queryString = { from: options.from, to: options.to, }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * Returns latest service configuration * * @param {string} serviceName */ function getConfigByServiceName(serviceName) { var url = host + '/services/' + serviceName + '/config'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * Returns service configuration for specified timestamp * * @param {string} serviceName * @param {string} timestamp */ function getConfigsByServiceNameAndTimestamp(serviceName, timestamp) { var url = host + '/services/' + serviceName + '/configs/' + timestamp + ''; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'GET' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * Removes service configuration (all versions) if no version is currently deployed * * @param {string} serviceName */ function deleteConfigByServiceName(serviceName) { var url = host + '/services/' + serviceName + '/config'; var queryString = { }; var requestOpts = { qs: queryString, uri: url, method: 'DELETE' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 204: return Promise.resolve(); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * Requests a new release * * @param {string} serviceName * @param {Object} options * @param {Object} options.data - The request body */ function postReleaseByServiceName(serviceName, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/services/' + serviceName + '/release'; var queryString = { }; var requestOpts = { qs: queryString, json: options.data, uri: url, method: 'POST' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } /** * Drops a release * * @param {string} serviceName * @param {Object} options * @param {Object} options.data - The request body */ function postDropByServiceName(serviceName, options) { // if options is not defined, set it options = options || {}; // otherwise if it's not the kind of object we want... if (!isObject(options)) { return Promise.reject(new Error('options parameter is required and must be a plain object')); } var url = host + '/services/' + serviceName + '/drop'; var queryString = { }; var requestOpts = { qs: queryString, json: options.data, uri: url, method: 'POST' }; return doRequest(requestOpts).then(function (serverResponse) { switch (serverResponse.statusCode) { case 200: return Promise.resolve(serverResponse.body); default: return Promise.reject(new Error('Unknown response code: ' + serverResponse.statusCode)); } }); } return { get: get, getByServiceName: getByServiceName, putConfigByServiceName: putConfigByServiceName, getConfigsByServiceName: getConfigsByServiceName, getConfigByServiceName: getConfigByServiceName, getConfigsByServiceNameAndTimestamp: getConfigsByServiceNameAndTimestamp, deleteConfigByServiceName: deleteConfigByServiceName, postReleaseByServiceName: postReleaseByServiceName, postDropByServiceName: postDropByServiceName, }; }()); this.Services = Services; } module.exports = IonrollerAPI;